package com.hcm.kernel.mvc.dao.impl;

import com.hcm.kernel.mvc.dao.IKernelDao;
import com.hcm.kernel.sql.Bean2SQL;
import com.hcm.kernel.sql.SQLWhere;
import com.hcm.kernel.util.Pagination;
import com.hcm.kernel.util.StringUtil;
import jodd.bean.BeanCopy;
import org.hibernate.*;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate4.HibernateCallback;
import org.springframework.orm.hibernate4.HibernateTemplate;
import org.springframework.stereotype.Repository;

import javax.persistence.Id;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * IkernelDao 实现类
 */
@Repository
public class KernelDaoImpl<T> implements IKernelDao<T> {

    private Class<T> clzz;
    private String keys;

    @Autowired
    public HibernateTemplate hibernateTemplate;

    public KernelDaoImpl() {
        // init clazz
        // 得到当前对象的所有实现接口类型
        Type types = this.getClass().getGenericSuperclass();
        // 将得到的type类型转化可参数化的类型
        if (types instanceof ParameterizedType) {
            ParameterizedType type = (ParameterizedType) types;
            // 得到泛型的类型的名字
            clzz = (Class<T>) type.getActualTypeArguments()[0];
            Method[] methods = clzz.getDeclaredMethods();
            for(Method method:methods){
                Annotation annotation = method.getAnnotation(Id.class);
                if(annotation != null){
                    keys = StringUtil.getFieldName(method.getName());
                    return;
                }
            }
        }
    }

    public T insert(T t) {
        hibernateTemplate.saveOrUpdate(t);
        return t;
    }

    public T delete(T t) {
        hibernateTemplate.delete(t);
        return t;
    }

    public T update(T t) {
        /*String getMethodName = StringUtil.getGetMethodName(keys);
        try {
            Method m = clzz.getDeclaredMethod(getMethodName);
            Object obj = m.invoke(t);
            if (obj != null) {
                T t1 = findById((Serializable) obj);
                BeanCopy.beans(clzz, t1).ignoreNulls(true).copy();
                hibernateTemplate.saveOrUpdate(t1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }*/
        hibernateTemplate.saveOrUpdate(t);
        return t;
    }

    public T findById(Serializable id) {
        return hibernateTemplate.get(clzz, id);
    }

    public List<T> findAll(Pagination page) {
        DetachedCriteria criteria = DetachedCriteria.forClass(clzz);
        if (page == null) {
            return (List<T>) hibernateTemplate.findByCriteria(criteria);
        } else {
            return (List<T>) hibernateTemplate.findByCriteria(criteria,
                    page.getStart(), page.getPageSize());
        }
    }

    public void deleteByProperties(final Map<String, Object> params) {
        hibernateTemplate.execute(new HibernateCallback<Integer>() {

            public Integer doInHibernate(Session session)
                    throws HibernateException {
                String hql = "delete from " + clzz.getSimpleName()
                        + " where 1=1 ";
                for (String key : params.keySet()) {
                    hql = hql + " and " + key + " = '" + params.get(key) + "'";
                }
                Query query = session.createQuery(hql);
                return query.executeUpdate();
            }
        });
    }

    public void deleteByWhere(final String where) {
        hibernateTemplate.execute(new HibernateCallback<Integer>() {

            public Integer doInHibernate(Session session)
                    throws HibernateException {
                String hql = "delete from " + clzz.getSimpleName()
                        + " where 1=1 " + where;

                Query query = session.createQuery(hql);
                return query.executeUpdate();
            }
        });
    }

    public Long countAll() {
        return hibernateTemplate.execute(new HibernateCallback<Long>() {

            public Long doInHibernate(Session session)
                    throws HibernateException {
                Criteria criteria = session.createCriteria(clzz);
                criteria.setProjection(Projections.alias(
                        Projections.count("id"), "cnt"));
                return (Long) criteria.uniqueResult();
            }

        });
    }

    public List<?> findByHql(final String hql, final Pagination page,
                             final Map<String, Object> params) {
        return hibernateTemplate.execute(new HibernateCallback<List<?>>() {

            public List<?> doInHibernate(Session session)
                    throws HibernateException {
                Query query = session.createQuery(hql);
                if (params != null)
                    query.setProperties(params);

                if (page != null) {
                    query.setFirstResult(page.getStart());
                    query.setMaxResults(page.getPageSize());
                }
                return query.list();
            }

        });
    }

    public Long countByHql(final String hql, final Map<String, Object> params) {
        return hibernateTemplate.execute(new HibernateCallback<Long>() {

            public Long doInHibernate(Session session)
                    throws HibernateException {
                Query query = session.createQuery(hql);
                if (params != null)
                    query.setProperties(params);

                return (Long) query.uniqueResult();
            }

        });
    }

    public List<?> findBySql(final String sql, final Pagination page,
                             final Map<String, Object> params) {
        return hibernateTemplate.execute(new HibernateCallback<List<?>>() {

            public List<?> doInHibernate(Session session)
                    throws HibernateException {
                SQLQuery query = session.createSQLQuery(sql);
                if (params != null)
                    query.setProperties(params);

                if (page != null) {
                    query.setFirstResult(page.getStart());
                    query.setMaxResults(page.getPageSize());
                }
                query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
                return query.list();
            }

        });
    }

    public Long countBySql(final String sql, final Map<String, Object> params) {
        return hibernateTemplate.execute(new HibernateCallback<Long>() {

            public Long doInHibernate(Session session)
                    throws HibernateException {
                SQLQuery query = session.createSQLQuery(sql);
                if (params != null)
                    query.setProperties(params);

                return (Long) query.uniqueResult();
            }

        });
    }

    public int execute(final String sql, final String type) {
        return hibernateTemplate.execute(new HibernateCallback<Integer>() {

            public Integer doInHibernate(Session session)
                    throws HibernateException {
                if (StringUtil.isBlank(type) || type.equals("sql")) {
                    SQLQuery query = session.createSQLQuery(sql);
                    return query.executeUpdate();
                } else {
                    Query query = session.createQuery(sql);
                    return query.executeUpdate();
                }
            }
        });
    }

    public List<T> findByEntity(T t,Pagination page) {
        String beanName = t.getClass().getSimpleName();
        String hql = "from " + beanName + " where 1=1 " ;

        return findByEntity(hql, t, page);
    }

    public long countByEntity(T t) {
        String beanName = t.getClass().getSimpleName();
        String hql = "select count("+keys+") from " + beanName + " where 1=1 ";
        return countByEntity(hql, t);
    }

    @SuppressWarnings("unchecked")
    public List<T> findByEntity(String hql, T t, Pagination page) {
        SQLWhere sw = Bean2SQL.getWhere(t, false);
        hql = hql + sw.getWhere();
        return (List<T>) findByHql(hql, page, sw.getParams());
    }

    public long countByEntity(String hql, T t) {
        checkKeys();
        SQLWhere sw = Bean2SQL.getWhere(t, false);
        hql = hql + sw.getWhere();
        return countByHql(hql, sw.getParams());
    }

    @Override
    public List<?> findByProperties(Pagination page, Map<String, Object>... properties) {
        String hql = "from " + clzz.getSimpleName() ;
        if(properties != null){
            hql = hql + " where 1=1 ";
            Set<String> set = properties[0].keySet();
            for(String key:set){
               hql = hql + " and " + key + " = :"+key;
            }
        }
        return findByHql(hql,page,properties[0]);
    }

    @Override
    public long countByProperties(Map<String, Object>... properties) {
        String hql = "select count("+keys+") from " + clzz.getSimpleName() ;
        if(properties != null){
            hql = hql + " where 1=1 ";
            Set<String> set = properties[0].keySet();
            for(String key:set){
                hql = hql + " and " + key + " = :"+key;
            }
        }
        return countByHql(hql, properties[0]);
    }

    public void checkKeys(){
        if(StringUtil.isBlank(keys)) throw new RuntimeException("请调用setKey设置主键后，才可以调用此方法，建议在构造方法中调用setKey。");
    }

    public T get(List<T> list){
        if(list == null || list.size() == 0) return null;
        return list.get(0);
    }


}
